﻿using System.Collections.Generic;
using Microsoft.Xna.Framework.Content.Pipeline.Graphics;

namespace ShevaContentPipeline.Processors.AnimatedModels
{
    /// <summary>
    /// Model splitter class.
    /// </summary>
    internal class ModelSplitter
    {
        /// <summary>Maximal bonuse count.</summary>
        private int _MaxBones = 20;
        /// <summary>All meshes.</summary>
        private List<MeshContent> _AllMeshes;
        /// <summary>Model root.</summary>
        private NodeContent _Root;
        /// <summary>Specifies if model was modified.</summary>
        private bool _ModelModified;

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="modelRoot">Model root.</param>
        /// <param name="maxBones">Maximal bones count.</param>
        public ModelSplitter(NodeContent modelRoot, int maxBones)
        {
            this._MaxBones = maxBones;
            this._Root = modelRoot;
            this._AllMeshes = new List<MeshContent>();
            this._ModelModified = false;
        }

        /// <summary>
        /// Method splits model.
        /// </summary>
        /// <returns></returns>
        public bool Split()
        {
            this._Process(_Root);

            return _ModelModified;
        }

        /// <summary>
        /// Process method.
        /// </summary>
        /// <param name="input"></param>
        private void _Process(NodeContent input)
        {
            _ProcessNode(input);
            foreach (MeshContent mesh in _AllMeshes)
                _ProcessMesh(mesh);

        }

        /// <summary>
        /// Method process mesh.
        /// </summary>
        /// <param name="mesh"></param>
        private void _ProcessMesh(MeshContent mesh)
        {
            if (MeshSplitter.NeedsSplitting(mesh, _MaxBones))
            {
                _ModelModified = true;
                MeshSplitter splitter = new MeshSplitter(mesh, _MaxBones);
                List<MeshContent> meshes = splitter.Split();
                foreach (MeshContent m in meshes)
                {
                    MeshHelper.MergeDuplicatePositions(m, 0);
                    MeshHelper.MergeDuplicateVertices(m);
                    MeshHelper.OptimizeForCache(m);
                }

                MeshContent firstMesh = meshes[0];
                NodeContent parent = mesh.Parent;
                List<NodeContent> children = new List<NodeContent>();
                foreach (NodeContent child in mesh.Children)
                    children.Add(child);

                foreach (NodeContent child in children)
                {
                    mesh.Children.Remove(child);
                }
                parent.Children.Remove(mesh);

                foreach (MeshContent m in meshes)
                {
                    parent.Children.Add(m);
                }
                foreach (NodeContent child in children)
                {
                    firstMesh.Children.Add(child);
                }
                foreach (NodeContent child in firstMesh.Children)
                {
                    _ProcessNode(child);
                }
            }
        }

        /// <summary>
        /// Method process node.
        /// </summary>
        /// <param name="input">Input node content.</param>
        private void _ProcessNode(NodeContent input)
        {
            if (input is MeshContent)
            {
                _AllMeshes.Add((MeshContent)input);
            }

            foreach (NodeContent child in input.Children)
            {
                _ProcessNode(child);
            }

        }

        /// <summary>
        /// Method gets weight channel.
        /// </summary>
        /// <param name="geom">Geometry.</param>
        /// <returns></returns>
        private VertexChannel<BoneWeightCollection> _GetWeightChannel(GeometryContent geom)
        {
            foreach (VertexChannel channel in geom.Vertices.Channels)
            {
                if (channel.Name == VertexChannelNames.Weights())
                {
                    return (VertexChannel<BoneWeightCollection>)channel;
                }
            }
            return null;
        }
    }
}
